﻿using System;

using System.Collections.Generic;
using System.Text;
using Silvermoon.Animations;
using System.Drawing;
using Silvermoon.Controls;

namespace Silvermoon.UI
{

    /// <summary>
    /// Specifies a transition to transition a property value within a given time.
    /// </summary>
    public abstract class Transition
    {
        /// <summary>
        /// Gets or sets a value that specifies how the Transition behaves after it reaches the end of its active period.
        /// </summary>
        public TrackMode Tracking = TrackMode.Reversible;

        /// <summary>
        /// Gets or sets the duration in miliseconds.
        /// </summary>
        public int Duration = -1;

        /// <summary>
        /// Gets or sets how long to delay in milliseconds before animation begins.
        /// </summary>
        public int Delay;

        /// <summary>
        /// Gets or sets the easing function. Default value is PowerEase.OutInstance.
        /// </summary>
        public IEasingFunction EasingFunction = PowerEase.Out;

        /// <summary>
        /// Gets or sets the properties for which animation occurs.
        /// </summary>
        public PropertyAccessor Property;

        internal abstract protected void BeginAnimation(IPropertyObject obj, bool useTransition);

        internal abstract protected void RemoveAnimation(IPropertyObject obj, int duration);

        internal protected void OnCompleted()
        {
            if (Completed != null) Completed(this, EventArgs.Empty);
        }

        /// <summary>
        /// Occurs when animation is completed or aborted.
        /// </summary>
        public event EventHandler Completed;

        public override int GetHashCode()
        {
            return Property.Index;
        }

        public override bool Equals(object obj)
        {
            return Property.Index == ((Transition)obj).Property.Index;
        }

    }

    public abstract class Transition<T> : Transition
    {
        protected internal override void BeginAnimation(IPropertyObject obj, bool useTransition)
        {
            if (Duration > 0 && useTransition)
            {
                Property.BeginAnimation(obj, this);
            }
            else
            {
                Property.BeginAnimation(obj, this, 0);
            }
        }

        protected internal override void RemoveAnimation(IPropertyObject obj, int duration)
        {
            Property.EndAnimation(obj, duration);
        }

        internal abstract object GetFrom();

        internal abstract object GetTo();
    }

    /// <summary>
    /// Specifies a timline where T is a value type and From To can be nullable.
    /// </summary>
    /// <typeparam name="T">Value type.</typeparam>
    public abstract class NullableTransition<T> : Transition<T> where T : struct
    {
        /// <summary>
        /// Gets the value to animate from, or null to use the current value of the property.
        /// </summary>
        public Nullable<T> From;

        /// <summary>
        /// Gets the value to animate to, or null to use the original value of the property.
        /// </summary>
        public Nullable<T> To;


        internal override object GetFrom()
        {
            return From;
        }

        internal override object GetTo()
        {
            return To;
        }
    }

    /// <summary>
    /// Specifies a timline where T is a reference type.
    /// </summary>
    /// <typeparam name="T">Reference type.</typeparam>
    public abstract class ObjectTransition<T> : Transition<T> where T : class
    {
        /// <summary>
        /// Gets the value to animate from, or null to use the current value.
        /// </summary>
        public T From;

        /// <summary>
        /// Gets the value to animate to or null to use the original value.
        /// </summary>
        public T To;

        internal override object GetFrom()
        {
            return From;
        }

        internal override object GetTo()
        {
            return To;
        }
    }

    public class IntTransition : NullableTransition<int>
    {
        public IntTransition()
            : base()
        {
        }

        public IntTransition(PropertyAccessor property, int duration, int? from, int? to)
            : this()
        {
            this.Property = property;
            this.Duration = duration;
            this.From = from;
            this.To = to;
        }

        public IntTransition(PropertyAccessor property, int duration, int? from, int? to, IEasingFunction ease)
            : this()
        {
            this.Property = property;
            this.Duration = duration;
            this.From = from;
            this.To = to;
            this.EasingFunction = ease;
        }
    }

    public class FloatTransition : NullableTransition<float>
    {
        public FloatTransition()
            : base()
        {
        }

        public FloatTransition(PropertyAccessor property, int duration, float? from, float? to)
            : this()
        {
            this.Property = property;
            this.Duration = duration;
            this.From = from;
            this.To = to;
        }

        public FloatTransition(PropertyAccessor property, int duration, float? from, float? to, IEasingFunction ease)
            : this()
        {
            this.Property = property;
            this.Duration = duration;
            this.From = from;
            this.To = to;
            this.EasingFunction = ease;
        }
    }

    public class SizeTransition : NullableTransition<Size>
    {
        public SizeTransition()
            : base()
        {
        }

        public SizeTransition(PropertyAccessor property, int duration, Size? from, Size? to)
            : this()
        {
            this.Property = property;
            this.Duration = duration;
            this.From = from;
            this.To = to;
        }

        public SizeTransition(PropertyAccessor property, int duration, Size? from, Size? to, IEasingFunction ease)
            : this()
        {
            this.Property = property;
            this.Duration = duration;
            this.From = from;
            this.To = to;
            this.EasingFunction = ease;
        }
    }

    public class PointTransition : NullableTransition<Point>
    {
        public PointTransition()
            : base()
        {
        }

        public PointTransition(PropertyAccessor property, int duration, Point? from, Point? to)
            : this()
        {
            this.Property = property;
            this.Duration = duration;
            this.From = from;
            this.To = to;
        }

        public PointTransition(PropertyAccessor property, int duration, Point? from, Point? to, IEasingFunction ease)
            : this()
        {
            this.Property = property;
            this.Duration = duration;
            this.From = from;
            this.To = to;
            this.EasingFunction = ease;
        }
    }


    public class PointFTransition : NullableTransition<PointF>
    {
        public PointFTransition()
            : base()
        {
        }

        public PointFTransition(PropertyAccessor property, int duration, PointF? from, PointF? to)
            : this()
        {
            this.Property = property;
            this.Duration = duration;
            this.From = from;
            this.To = to;
        }

        public PointFTransition(PropertyAccessor property, int duration, PointF? from, PointF? to, IEasingFunction ease)
            : this()
        {
            this.Property = property;
            this.Duration = duration;
            this.From = from;
            this.To = to;
            this.EasingFunction = ease;
        }
    }

    public class RectTransition : NullableTransition<Rectangle>
    {
        public RectTransition()
            : base()
        {
        }

        public RectTransition(PropertyAccessor property, int duration, Rectangle? from, Rectangle? to)
            : this()
        {
            this.Property = property;
            this.Duration = duration;
            this.From = from;
            this.To = to;
        }

        public RectTransition(PropertyAccessor property, int duration, Rectangle? from, Rectangle? to, IEasingFunction ease)
            : this()
        {
            this.Property = property;
            this.Duration = duration;
            this.From = from;
            this.To = to;
            this.EasingFunction = ease;
        }
    }

    public class ColorTransition : NullableTransition<Color>
    {
        public ColorTransition()
            : base()
        {
            Property = Control.ColorProperty;
        }

        public ColorTransition(int duration, Color? from, Color? to)
            : this()
        {
            this.Duration = duration;
            this.From = from;
            this.To = to;
        }

        public ColorTransition(PropertyAccessor property, int duration, Color? from, Color? to)
            : this()
        {
            this.Property = property;
            this.Duration = duration;
            this.From = from;
            this.To = to;
        }

        public ColorTransition(int duration, Color from, Color to, IEasingFunction ease)
            : this()
        {
            this.Duration = duration;
            this.From = from;
            this.To = to;
            this.EasingFunction = ease;
        }

    }

    public class BoolTransition : NullableTransition<bool>
    {
       public BoolTransition()
            : base()
        {
        }

       public BoolTransition(PropertyAccessor property, int duration, bool? from, bool? to)
            : this()
        {
            this.Property = property;
            this.Duration = duration;
            this.From = from;
            this.To = to;
        }   
    }

    public class TranslateTransition : ObjectTransition<TranslateTransform>
    {
        public TranslateTransition()
            : base()
        {
            Property = Control.TranslateProperty;
        }

        public TranslateTransition(int duration, TranslateTransform from, TranslateTransform to)
            : this()
        {
            this.Duration = duration;
            this.From = from;
            this.To = to;
        }

        public TranslateTransition(int duration, TranslateTransform from, TranslateTransform to, IEasingFunction ease)
            : this()
        {
            this.Duration = duration;
            this.From = from;
            this.To = to;
            this.EasingFunction = ease;
        } 
    }

    public class ScaleTransition : ObjectTransition<ScaleTransform>
    {
        public ScaleTransition()
            : base()
        {
            Property = Control.ScaleProperty;
        }


        public ScaleTransition(int duration, ScaleTransform from, ScaleTransform to)
            : this()
        {
            this.Duration = duration;
            this.From = from;
            this.To = to;
        }

        public ScaleTransition(int duration, ScaleTransform from, ScaleTransform to, IEasingFunction ease)
            : this()
        {
            this.Duration = duration;
            this.From = from;
            this.To = to;
            this.EasingFunction = ease;
        }  
    }
}
